/*
 * Copyright (C) 2008 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.zxinglibrary.zxing.camera;

import android.content.Context;
import android.hardware.Camera;
import android.os.Handler;
import android.view.SurfaceHolder;

import java.io.IOException;
import java.util.List;


/**
 * 邮箱: ning137@foxmail.com
 *
 * 作者: njb
 *
 * 日期: 2017年2月10日
 *
 * 描述: This object wraps the Camera service object and expects to be the only one
 * talking to it. The implementation encapsulates the steps needed to take
 * preview-sized images, which are used for both preview and decoding.
 *
 */
@SuppressWarnings("deprecation")
public class CameraManager {

	private static CameraManager sCameraManager;

	private final CameraConfigurationManager mConfigManager;
	private Camera mCamera;
	private boolean mInitialized;
	private boolean mPreviewing;
	/**
	 * Preview frames are delivered here, which we pass on to the registered handler. Make sure to clear the handler so
	 * it will only receive one message.
	 */
	private final PreviewCallback mPreviewCallback;
	/** Auto-focus callbacks arrive here, and are dispatched to the Handler which requested them. */
	private final AutoFocusCallback mAutoFocusCallback;

	/**
	 * Initializes this static object with the Context of the calling Activity.
	 */
	public static void init(Context context) {
		if (sCameraManager == null) {
			sCameraManager = new CameraManager(context);
		}
	}

	/**
	 * Gets the CameraManager singleton instance.
	 *
	 * @return A reference to the CameraManager singleton.
	 */
	public static CameraManager get() {
		return sCameraManager;
	}

	private CameraManager(Context context) {
		this.mConfigManager = new CameraConfigurationManager(context);
		mPreviewCallback = new PreviewCallback(mConfigManager);
		mAutoFocusCallback = new AutoFocusCallback();
	}

	/**
	 * Opens the mCamera driver and initializes the hardware parameters.
	 *
	 * @param holder The surface object which the mCamera will draw preview frames into.
	 * @throws IOException Indicates the mCamera driver failed to open.
	 */
	public void openDriver(SurfaceHolder holder) throws IOException {
		if (mCamera == null) {
			mCamera = Camera.open();
			if (mCamera == null) {
				throw new IOException();
			}
			mCamera.setPreviewDisplay(holder);

			if (!mInitialized) {
				mInitialized = true;
				mConfigManager.initFromCameraParameters(mCamera);
			}
			mConfigManager.setDesiredCameraParameters(mCamera);
		}
	}

	/**
	 * 打开或关闭闪光灯
	 *
	 * @param open 控制是否打开
	 * @return 打开或关闭失败，则返回false。
	 */
	public boolean setFlashLight(boolean open) {
		if (mCamera == null) {
			return false;
		}
		Camera.Parameters parameters = mCamera.getParameters();
		if (parameters == null) {
			return false;
		}
		List<String> flashModes = parameters.getSupportedFlashModes();
		// Check if camera flash exists
		if (null == flashModes || 0 == flashModes.size()) {
			// Use the screen as a flashlight (next best thing)
			return false;
		}
		String flashMode = parameters.getFlashMode();
		if (open) {
			if (Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode)) {
				return true;
			}
			// Turn on the flash
			if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
				parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
				mCamera.setParameters(parameters);
				return true;
			} else {
				return false;
			}
		} else {
			if (Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) {
				return true;
			}
			// Turn on the flash
			if (flashModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {
				parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
				mCamera.setParameters(parameters);
				return true;
			} else
				return false;
		}
	}

	/**
	 * Closes the camera driver if still in use.
	 */
	public void closeDriver() {
		if (mCamera != null) {
			mCamera.release();
			mInitialized = false;
			mPreviewing = false;
			mCamera = null;
		}
	}

	/**
	 * Asks the mCamera hardware to begin drawing preview frames to the screen.
	 */
	public void startPreview() {
		if (mCamera != null && !mPreviewing) {
			mCamera.startPreview();
			mPreviewing = true;
		}
	}

	/**
	 * Tells the mCamera to stop drawing preview frames.
	 */
	public void stopPreview() {
		if (mCamera != null && mPreviewing) {
			mCamera.stopPreview();
			mPreviewCallback.setHandler(null, 0);
			mAutoFocusCallback.setHandler(null, 0);
			mPreviewing = false;
		}
	}

	/**
	 * A single preview frame will be returned to the handler supplied. The data will arrive as byte[] in the
	 * message.obj field, with width and height encoded as message.arg1 and message.arg2, respectively.
	 *
	 * @param handler The handler to send the message to.
	 * @param message The what field of the message to be sent.
	 */
	public void requestPreviewFrame(Handler handler, int message) {
		if (mCamera != null && mPreviewing) {
			mPreviewCallback.setHandler(handler, message);
			mCamera.setOneShotPreviewCallback(mPreviewCallback);
		}
	}

	/**
	 * Asks the mCamera hardware to perform an autofocus.
	 *
	 * @param handler The Handler to notify when the autofocus completes.
	 * @param message The message to deliver.
	 */
	public void requestAutoFocus(Handler handler, int message) {
		if (mCamera != null && mPreviewing) {
			mAutoFocusCallback.setHandler(handler, message);
			// Log.d(TAG, "Requesting auto-focus callback");
			mCamera.autoFocus(mAutoFocusCallback);
		}
	}
}
